import { Types } from "cat";

type SC_setValue = (key: string, value: unknown) => Promise<boolean>;

type SC_addValueChangeListener = (
  name: string,
  callback: Types.Vcl.cbFunction,
) => number;

type SC_removeValueChangeListener = (
  id: ReturnType<SC_addValueChangeListener>,
) => void;

type SC_getResourceURL = (
  name: string,
  isBlobUrl?: boolean,
) => `${"blob" | "data"}:${string}` | undefined;
type SC_getResourceText = (name: string) => string | undefined;

type SC_openInTab_O = (
  url: string,
  options?: Types.OpenTab.Option,
) => Types.OpenTab.Result;

type SC_openInTab_L = (
  url: string,
  loadInBackground?: boolean,
) => Types.OpenTab.Result;

type SC_registerMenuCommand = (
  name: string,
  fn: () => void | Promise<void>,
  accessKey?: string,
) => number;
type SC_unregisterMenuCommand = (menuCmdId: number) => void;

type SC_xmlhttpRequest = (details: Types.Xhr.Details) => CM_Types.Xhr.Return;
type SC_download_D = (details: Types.Download.Details) => CM_Types.Xhr.Return;
type SC_download_U = (
  url: Types.Download.Details["url"],
  name: Types.Download.Details["name"],
) => CM_Types.Xhr.Return;

type SC_addStyle = (css: string) => HTMLStyleElement;

type SC_Notification_O = (
  options: Types.Notification.Option,
  ondone?: (this: Record<"id", string>, clicked: boolean, id: string) => void,
) => Promise<void>;

type SC_Notification_T = (
  text: Types.Notification.Option["text"],
  title?: Types.Notification.Option["title"],
  image?: Types.Notification.Option["image"],
  onclick?: (this: Record<"id", string>, id: string, index?: number) => void,
) => Promise<void>;

type SC_closeNotification = (id: string) => void;
type SC_updateNotification = (
  id: string,
  options: Types.Notification.Option,
) => void;

type SC_setClipboard = (
  data: string,
  info?: Types.ClipboardDataType | string,
) => void;

type SC_cookie = {
  (
    method: Types.Cookie.Method,
    details:
      | Types.Cookie.SetDetails
      | Types.Cookie.ListDetails
      | Types.Cookie.DeleteDetails,
    callback: Types.Cookie.Callback,
  ): void;
  list: (
    details: Types.Cookie.ListDetails,
    callback: Types.Cookie.Callback,
  ) => void;
  set: (
    details: Types.Cookie.SetDetails,
    callback: Types.Cookie.Callback,
  ) => void;
  delete: (
    details: Types.Cookie.DeleteDetails,
    callback: Types.Cookie.Callback,
  ) => void;
};

type SC_log = (
  message: string,
  level: "debug" | "info" | "warn" | "error",
) => void;

declare function CAT_fileStorage(
  action: "list",
  details: {
    onload?: (files: Types.FileStorage.FileInfo[]) => void;
    onerror?: (error: Types.FileStorage.Error) => void;
  },
): void;
declare function CAT_fileStorage(
  action: "download",
  details: {
    file: Types.FileStorage.FileInfo;
    onload: (data: Blob) => void;
    onerror?: (error: Types.FileStorage.Error) => void;
  },
): void;
declare function CAT_fileStorage(
  action: "delete",
  details: {
    path: string;
    onload?: () => void;
    onerror?: (error: Types.FileStorage.Error) => void;
  },
): void;
declare function CAT_fileStorage(
  action: "upload",
  details: {
    path: string;
    data: Blob;
    onload?: () => void;
    onerror?: (error: Types.FileStorage.Error) => void;
  },
): void;

interface SC_ScriptInfo {
  readonly author: string;
  readonly connects: string[];
  readonly description?: string;
  readonly grant: string[];
  readonly header: string;
  readonly icon: string | null;
  readonly icon64: string | null;
  readonly includes: string[];
  readonly matches: string[];
  readonly name: string;
  readonly namespace: string;
  readonly "run-at": `document-${"start" | "body" | "end" | "idle"}`;
  readonly version: string;
}

interface SC_Info {
  readonly script: SC_ScriptInfo;
  readonly scriptHandler: "ScriptCat";
  readonly scriptMetaStr: string;
  readonly userConfig?: Record<string, Record<string, Types.UserConfig.Config>>;
  readonly userConfigStr: string;
  readonly scriptUpdateURL?: string;
  readonly scriptWillUpdate: boolean;
  readonly version: string;
}

interface SC_Object {
  getValue: CM_getValue;
  setValue: SC_setValue;
  listValues: CM_listValues;
  deleteValue: CM_deleteValue;
  addValueChangeListener: SC_addValueChangeListener;
  removeValueChangeListener: SC_removeValueChangeListener;
  getResourceURL: SC_getResourceURL;
  getResourceText: SC_getResourceText;
  openInTab: SC_openInTab_O & SC_openInTab_L;
  getTab: CM_getTab;
  saveTab: CM_saveTab;
  getTabs: CM_getTabs;
  registerMenuCommand: SC_registerMenuCommand;
  unregisterMenuCommand: SC_unregisterMenuCommand;
  xmlHttpRequest: SC_xmlhttpRequest;
  download: SC_download_D & SC_download_U;
  addStyle: SC_addStyle;
  notification: SC_Notification_O & SC_Notification_T;
  setClipboard: SC_setClipboard;
  cookie: Omit<SC_cookie, "list" | "set" | "delete">;
  log: SC_log;
}

interface SC_SafeWindow extends Window {
  external: External & {
    Tampermonkey?: {
      isInstalled: (
        name: string,
        namespace: string,
        callback: (info: { installed: boolean; version?: string }) => void,
      ) => void;
    };
    Scriptcat?: {
      isInstalled: (
        name: string,
        namespace: string,
        callback: (info: { installed: boolean; version?: string }) => void,
      ) => void;
    };
  };
}

declare module "cat" {
  export const GM: SC_Object;
  export const GM_getValue: CM_getValue;
  export const GM_setValue: SC_setValue;
  export const GM_listValues: CM_listValues;
  export const GM_deleteValue: CM_deleteValue;
  export const GM_addValueChangeListener: SC_addValueChangeListener;
  export const GM_removeValueChangeListener: SC_removeValueChangeListener;
  export const GM_getResourceURL: SC_getResourceURL;
  export const GM_getResourceText: SC_getResourceText;
  export const GM_openInTab: SC_openInTab_O & SC_openInTab_L;
  export const GM_getTab: Promisize<CM_getTab>;
  export const GM_saveTab: PromisizeReturn<CM_saveTab, boolean>;
  export const GM_getTabs: Promisize<CM_getTabs>;
  export const GM_registerMenuCommand: SC_registerMenuCommand;
  export const GM_unregisterMenuCommand: SC_unregisterMenuCommand;
  export const GM_xmlhttpRequest: SC_xmlhttpRequest;
  export const GM_download: SC_download_D & SC_download_U;
  export const GM_addStyle: SC_addStyle;
  export const GM_notification: SC_Notification_O & SC_Notification_T;
  export const GM_closeNotification: SC_closeNotification;
  export const GM_updateNotification: SC_updateNotification;
  export const GM_setClipboard: SC_setClipboard;
  export const GM_cookie: SC_cookie;
  export const GM_log: SC_log;
  export const CAT_userConfig: () => void;
  export const GM_info: SC_Info;
  export let unsafeWindow: unsafeWindow;
}
